package game.entities.enemies;

import static org.lwjgl.opengl.GL11.*;
import java.awt.Rectangle;
import java.util.Random;

import application.Configuration;
import application.Window;
import application.messages.commands.parameters.Coordinates2D;

import game.ICollidable;
import game.camera.Camera;
import game.entities.IEnemyEntity;

/**
 * Implements simple enemy entity.
 * 
 * @author Tomas Zima
 */
public class GeneralEnemy implements IEnemyEntity, ICollidable
{
	/**
	 * Constructor.
	 */
	public GeneralEnemy()
	{
		//
	}
	
	/**
	 * Constructor.
	 * Saves coordinates of enemy.
	 * 
	 * @param x
	 * @param y
	 */
	public GeneralEnemy(float x, float y)
	{
		this.setXY(x, y);
	}
	
	/**
	 * Returns bounding boxes of the enemy
	 * (for collision checker).
	 */
	@Override
	public Rectangle[] getBoundingBoxes()
	{
		Rectangle result[] = new Rectangle[1];
		
		result[0]			= new Rectangle();
		
		result[0].x			= (int) positionX;
		result[0].y			= (int) positionY;
		result[0].width		= width;
		result[0].height	= height;
		
		return result;
	}

	/**
	 * Called if enemy is in the collision.
	 */
	@Override
	public void removeCollision()
	{
		popLastPosition();
	}

	/**
	 * Called if enemy is not in the collision.
	 */
	@Override
	public void notInCollision()
	{
		pushLastPosition();
	}

	/**
	 * Called if enemy collides from down side.
	 */
	@Override
	public void collidesDown()
	{
		// TODO Auto-generated method stub
	}

	/**
	 * Called if enemy does not collide from down side.
	 */
	@Override
	public void notCollidesDown()
	{
		// TODO Auto-generated method stub
	}

	/**
	 * Called to update an enemy.
	 * 
	 * @param deltaTime
	 * 	Time which is gone from the last call.
	 * 
	 * @param playerX
	 * 	Coordinate of player in the X axis.
	 * 
	 * @param playerY
	 * 	Coordinate of player in the Y axis.
	 */
	@Override
	public void update(long deltaTime, int playerX, int playerY)
	{
		// Random numbers generator
		Random randomizer = new Random();
		
		// Update ways?
		if (lastWayChange + randomizer.nextInt(5) * 1000 < System.currentTimeMillis())
		{
			// Temporary variables for control
			int tmp1 = randomizer.nextInt(3);
			int tmp2 = randomizer.nextInt(3);
			
			/*
			 * X axis.
			 */
			// Move to the player
			if (tmp1 != 0)
			{
				// to the right
				if (positionX < playerX)
				{
					this.leftRightWay = true;
				}
				// to the left
				else
				{
					this.leftRightWay = false;
				}
			}
			// Move from the player
			else
			{
				// to the left
				if (positionX > playerX)
				{
					this.leftRightWay = false;
				}
				// to the right
				else
				{
					this.leftRightWay = true;
				}
			}
			
			/**
			 * Y axis.
			 */
			// To the player
			if (tmp2 != 0)
			{
				// up
				if (positionY < playerY)
				{
					this.upDownWay = true;
				}
				// down
				else
				{
					this.upDownWay = false;
				}
			}
			// from the player
			else
			{
				// down
				if (positionY > playerY)
				{
					this.upDownWay = false;
				}
				// up
				else
				{
					this.upDownWay = true;
				}
			}

			// start measuring for another update
			lastWayChange = System.currentTimeMillis();
		}
		
		// Randomly choose size of the movement
		int movement = (randomizer.nextInt(1) + 1);
		
		switch (this.type)
		{
			case 0:
				movement *= 20;
				break;
				
			case 1:
				movement *= 50;
				break;
		}
		
		/*
		 * Move with enemy based on the way (X axis).
		 */
		// to the right
		if (leftRightWay)
		{
			this.positionX += (deltaTime / 1000.0f) * 5 * movement;
		}
		// to the left
		else
		{
			this.positionX -= (deltaTime / 1000.0f) * 5 * movement;
		}
		
		/*
		 * Move with enemy based on the way (Y axis).
		 */
		// down
		if (upDownWay)
		{
			this.positionY += (deltaTime / 1000.0f) * movement;
		}
		// up
		else
		{
			this.positionY -= (deltaTime / 1000.f) * movement;
		}
	}
	
	/**
	 * Redraw enemy.
	 * 
	 * @param cameraX
	 * @Param cameraY
	 */
	@Override
	public void redraw(int cameraX, int cameraY)
	{
		// Save state
		boolean texColMix	= Window.isTexColorMixingEnabled();
		boolean texMix		= Window.isTexturingEnabled();
		
		// Set window properties
		Window.disableTexColorMixing();
		Window.disableTexturing();

		// Coordinates for drawing
		int relX = (int) Camera.getPositionX((int) positionX, (int) cameraX);
		int relY = (int) Camera.getPositionY((int) positionY, (int) cameraY);

		// Draw
		switch (this.type)
		{
			case 0:
				glColor3f(0.7f, 0.0f, 0.0f);
				break;
				
			case 1:
				glColor3f(0.0f, 0.0f, 0.0f);
				break;
		}
		
		glBegin(GL_QUADS);
			glVertex2i(relX, relY);
			glVertex2i(relX + width, relY);
			glVertex2i(relX + width, relY + height);
			glVertex2i(relX, relY + height);
		glEnd();
		
		// Restore window settings
		if (texColMix) Window.enableTexColorMixing();
		if (texMix) Window.enableTexturing();
	}
	
	/**
	 * Push last position of the enemy.
	 */
	private void pushLastPosition()
	{
		this.lastPositionX = positionX;
		this.lastPositionY = positionY;
	}
	
	/**
	 * Pop last valid position of the enemy.
	 */
	private void popLastPosition()
	{
		this.positionX = lastPositionX;
		this.positionY = lastPositionY;
	}
	
	/**
	 * Set position of the enemy.
	 * It has to be stored as the float because of the
	 * calculating with size of movements based on time.
	 * 
	 * @param x
	 * @param y
	 */
	public final void setXY(float x, float y)
	{
		this.positionX = x;
		this.positionY = y;
		
		pushLastPosition();
	}

	/**
	 * Checks if object can collide with a map.
	 */
	@Override
	public final boolean isCollidableWithMap()
	{
		return true;
	}
	
	/**
	 * Checks if object is sensitively destroyable
	 * (it will be destroyed by collision with other object).
	 */
	@Override
	public final boolean isSensitivelyDestroyable()
	{
		return false;
	}
	
	/**
	 * Destroy object.
	 */
	@Override
	public void destroy()
	{
		this.destroyed = true;
	}

	/**
	 * Checks if collision with other object will destroy
	 * it (for shots etc).
	 */
	@Override
	public boolean hasDestroyingCharacter()
	{
		return false;
	}

	/**
	 * Checks if object can be damaged.
	 */
	@Override
	public boolean isDamagable()
	{
		return true;
	}
	
	/**
	 * Damage this object. Maximal count of lives is 100, minimal count
	 * is zero and it is decimal, non-pointing number.
	 */
	@Override
	public void damage(int lives)
	{
		this.destroy();
	}
	
	/**
	 * Checks if object was destroyed or not.
	 */
	@Override
	public boolean isDestroyed()
	{
		return this.destroyed;
	}
	
	/**
	 * Checks if object is manually controlled by player
	 * or not.
	 */
	@Override
	public boolean isPlayerControlled()
	{
		return false;
	}
	
	/**
	 * Checks if object is an enemy.
	 */
	@Override
	public boolean isEnemy()
	{
		return true;
	}
	
	/**
	 * Returns current position of a object.
	 */
	@Override
	public Coordinates2D getPosition()
	{
		return new Coordinates2D((int) this.positionX, (int) this.positionY);
	}

	// Last position of the enemy
	// (return player back to correct position if it collides)
	private float lastPositionX;
	private float lastPositionY;
	
	// Current position of the enemy
	private float positionX;
	private float positionY;

	// Size of enemy
	private int width	= 32;
	private int height	= 64;

	// Way of the movement
	private boolean leftRightWay = true;
	private boolean upDownWay = true;
	
	// Last update of the way of the movement
	private long lastWayChange = System.currentTimeMillis();
	
	// Was object destroyed?
	private boolean destroyed = false;
	
	// Type of enemy
	private int type = new Random().nextInt(2);
}
